// Copyright (C) 2007 Google Inc. and Georges Harik
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Georges Harik and Noam Shazeer

#ifndef _EXECUTION_H_
#define _EXECUTION_H_

#include "blackboard.h"

#define MACHINE_FAIL CHECK(false)


struct Element;
struct Execution;

struct Thread {
  Thread() {
    element_ = NULL;
    execution_ = NULL;
  }
  Thread(const Thread& t) {
    stack_ = t.stack_;
    element_ = t.element_;
    execution_ = t.execution_;
  }
  Tuple stack_;
  Element * element_;
  Execution * execution_;
  string ToString();
};

struct OnSubscription : public Blackboard::Subscription {
  // t should point to the child of the On.
  void Init(const Thread & t, Blackboard::Row *row);
  void Update(Blackboard::Row *rw, int tuple_num);
  // data
  Thread thread_;
};

struct Execution : public Base {
  void Init() { 
    Base::Init(); 
    current_time_ = Time();
    blackboard_ = New<Blackboard>();
    guide_ = NULL;
    total_bias_ = 0;
    choice_counter_ = 0;
  }

  void AddGuide() {
    CHECK(guide_ == NULL);
    guide_ = New<Execution>();
  }

  Base::Type GetBaseType() const { return Base::EXECUTION; }
  
  void ParseAndExecute(const Tuple & program_tuple, 
		       bool pretty = true, 
		       bool execute = true);

  void ExecuteForever() {
    while (run_queue_.size() || post_queue_.size() || unpost_queue_.size()) 
      ExecuteOneEpoch();
  }

  void ExecuteOneEpoch() {
    if (guide_)
      guide_->ExecuteForever();
    ExecuteRunnableThreads();
    CommitChanges();
  }

  void CommitChanges() {
    forall(run, unpost_queue_) {
      // cout << "Committing " << *run << " at " << current_time_ << endl;
      blackboard_->Remove(*run);
      if (guide_)
	guide_->blackboard_->Remove(*run);
    }
    forall(run, post_queue_) {
      // cout << "Committing " << *run << " at " << current_time_ << endl;
      blackboard_->Post(*run);
      if (guide_)
	guide_->blackboard_->Post(*run);
    }
    unpost_queue_.clear();
    post_queue_.clear();
  }

  void ExecuteRunnableThreads();

  void AddCodeTreeToRun(Element *top_element);

  // The thread points at the immediately executable code
  static Tuple MatchAndRun(Thread & thread, const Tuple & variable_tuple);

  void Enqueue(const Thread & t, int64 time_delay_dimension) {
    // VLOG(2) << "Enqueue Thread " << t.ToString() << endl;
    run_queue_[time_delay_dimension].push_back(t);
  }
  
  void AddPost(const Tuple & t) { post_queue_.push_back(t);}
  void AddUnpost(const Tuple & t) { unpost_queue_.push_back(t); }
  
  Record GetRecordForDisplay() const;

  // the static program.
  vector<Element *> top_elements_;

  // these are all the subscriptions generated by executing on statements.
  set<OnSubscription *> subscriptions_;

  // This contains threads ordered by when they can be run
  map<int64, vector<Thread> > run_queue_;

  // This contains the list of tuples to be posted at the end
  // of this epoch
  vector<Tuple> post_queue_;
  vector<Tuple> unpost_queue_;

  // Guide execution if it exists
  Execution * guide_;

  // This is the blackboard for the execution
  Blackboard *blackboard_;

  // The current time (Tracked during execution for no purpose)
  Time current_time_;

  // the output of the program
  string output_; 

  // the log likelihood of the execution
  double total_bias_;

  // Flakes that the new flake chooser has already chosen and cannot
  // choose again.
  rankset<Flake> existing_flakes_;
  int64 choice_counter_;
};

Execution * Execute(const Tuple & main_program, const Tuple & guide_program,
		    bool pretty_parsing = true);
// appends to result tuple
void ReadCodeFile(string filename, Tuple *result);

  
#endif
